import { eduApi } from './edu-api'
import AgoraRTCClient from './agora-rtc-client'
import AgoraRTMClient, { ChatCmdType, CoVideoType, RoomMessage } from './agora-rtm-client'
import { globalStore } from './global'
import { t } from '../i18n/i18n'

/* const AgoraElectronClient = null
const isElectron = false */
export const platform = 'web'

export class AgoraUser {
  uid
  account
  role
  video
  audio
  chat
  grantBoard
  userId // 仅用于服务端
  screenId //仅用于屏幕共享
}

export class Me extends AgoraUser {
  rtmToken
  rtcToken
  channelName
  screenToken
  appID
  coVideo
  uuid
}

export class ClassState {
  rid
  roomName
  teacherId
  boardId // whiteboard_uuid
  boardToken // whiteboard_token
  // edu roomId
  // 房间id
  roomId

  // edu roomType
  // 房间类型
  roomType

  // lock board
  // 锁定
  lockBoard // lock_board

  // start class
  // 开始上课
  courseState
  // mute all chat
  // 全员禁言
  muteChat
  // recording
  recordId
  recordingTime
  isRecording
  screenId
  screenToken
  coVideoUids
  memberCount
}

export class RtcState {
  published
  joined
  users
  shared
  localStream = null
  localSharedStream = null
  remoteStreams
}

export class MediaDeviceState {
  microphoneId
  speakerId
  cameraId
  speakerVolume
  camera
  microphone
  speaker
}

export class SessionInfo {
  uid
  rid
  account
  roomName
  roomType
  role
}

export class RtmState {
  joined
}

export class UserParams {
  userId
  account
}

export class ChatMessage {
  account
  text
  link
  ts
  id
}

export class AgoraStream {
  stream
  streamID
  local
}

export const RoomState = {
  rtmLock: false,
  rtmToken: '',
  recordLock: false,
  rtcToken: '',
  appID: '',
  me: {},
  users: [],
  course: {},
  applyUser: {},
  rtc: {},
  rtm: {},
  mediaDevice: {},
  messages: [],
  language: ''
}

export const AgoraMediaStream = {
  streamID: '',
  stream: {}
}

export class RoomStore {
  _state = RoomState

  get state() {
    return this._state
  }

  set state(newState) {
    this._state = newState
  }

  defaultState = {
    rtmLock: false,
    rtcToken: '',
    rtmToken: '',
    appID: '',
    recordLock: false,
    me: {
      account: '',
      uid: '',
      roomToken: '',
      role: 0,
      video: 1,
      audio: 1,
      chat: 1,
      grantBoard: 0,
      rtmToken: '',
      rtcToken: '',
      appID: '',
      coVideo: ''
    },
    users: [],
    applyUser: {
      userId: '',
      account: ''
    },
    rtm: {
      joined: false
    },
    rtc: {
      published: false,
      joined: false,
      shared: false,
      users: [],
      localStream: null,
      localSharedStream: null,
      remoteStreams: []
    },
    course: {
      teacherId: '',
      boardId: '',
      boardToken: '',
      courseState: 0,
      muteChat: 0,
      isRecording: false,
      recordId: '',
      recordingTime: 0,
      rid: '',
      roomName: '',
      roomType: 0,
      lockBoard: 0,
      roomId: '',
      screenId: '',
      screenToken: ''
    },
    mediaDevice: {
      microphoneId: '',
      speakerId: '',
      cameraId: '',
      speakerVolume: 100,
      camera: 0,
      speaker: 0,
      microphone: 0
    },
    messages: [],
    ...JSON.parse(sessionStorage.getItem('agora_room'))
  }

  rtmClient = new AgoraRTMClient()
  rtcClient = new AgoraRTCClient()

  constructor() {
    this._state = {
      ...this.defaultState
    }
  }
  // // 进入房间
  async getLiveRoomInfo(id) {
    const data = await eduApi.getLiveRoomInfo(id)
    return data
  }

  // 进入房间
  async joinChannel({ uid, channel, dual, token, appId }) {
    await this.rtcClient.initClient({ uid, channel, token, appId })
    // await this.rtcClient.join(+uid, channel, token)
    dual && (await this.rtcClient.enableDualStream())
    this.setRTCJoined(true)
    const mediaDevice = this.state.mediaDevice
    const streamSpec = {
      streamID: uid,
      video: true,
      audio: true,
      mirror: false,
      screen: false,
      microphoneId: mediaDevice.microphoneId,
      cameraId: mediaDevice.cameraId,
      audioOutput: {
        volume: mediaDevice.speakerVolume,
        deviceId: mediaDevice.speakerId
      }
    }
    let canPublish = this.state.me.coVideo
    if (canPublish) {
      this.rtcClient.publishLocalStream(streamSpec)
    }
  }

  async leaveChannel() {
    this.localUid = 0
    this.channel = ''
    try {
      // await this.rtcClient.unpublishLocalStream()
      await this.rtcClient.exit()
      await this.rtmClient.exit()
      this.rtcClient._joined = false
      this.setRTCJoined(false)
    } catch (err) {
      console.error(err)
    } finally {
      this.rtcClient.destroy()
      this.rtcClient.destroyClient()
    }
  }

  // 登录房间
  async LoginToRoom(payload) {
    const { userName, roomName, role, type, uuid } = payload
    try {
      const res = await eduApi.Login({
        userName,
        roomName,
        role,
        type,
        roomUuid: roomName,
        userUuid: uuid
      })
      const { course, me, users: rawUsers, appID, onlineUsers } = res
      let users = []
      rawUsers.forEach(it => {
        users.push({
          role: it.role,
          account: it.userName,
          uid: it.uid,
          video: it.enableVideo,
          audio: it.enableAudio,
          chat: it.enableChat,
          grantBoard: it.grantBoard,
          userId: it.userId,
          screenId: it.screenId
        })
      })
      await this.rtmClient.login(appID, `${me.uid}`, me.rtmToken)
      await this.rtmClient.join(course.rid)
      this.state = {
        ...this.state,
        rtm: {
          ...this.state.rtm,
          joined: true
        },
        course: {
          ...this.state.course,
          rid: course.channelName,
          roomType: course.roomType,
          roomId: course.roomId,
          roomName: course.roomName,
          courseState: course.courseState,
          muteChat: course.muteAllChat,
          recordId: `${course.recordId}`,
          isRecording: course.isRecording,
          recordingTime: course.recordingTime,
          lockBoard: course.lockBoard,
          boardId: `${course.boardId}`,
          boardToken: `${course.boardToken}`,
          teacherId: `${course.teacherId}`,
          screenId: `${me.screenId}`,
          screenToken: `${me.screenToken}`,
          coVideoUids: course.coVideoUids,
          startTime: course.startTime,
          memberCount: +onlineUsers
        },
        me: {
          ...this.state.me,
          uid: me.uid,
          account: me.userName,
          rtmToken: me.rtmToken,
          rtcToken: me.rtcToken,
          channelName: me.channelName,
          screenId: me.screenId,
          screenToken: me.screenToken,
          appID: me.appID,
          role: me.role,
          chat: me.enableChat,
          video: me.enableVideo,
          audio: me.enableAudio,
          userId: me.userId,
          coVideo: me.coVideo
        },
        users,
        appID
      }
      return this.state
    } catch (err) {
      if (this.rtmClient._logged) {
        this.rtmClient.logout()
      }
    }
  }

  setRTCJoined(joined) {
    this.state = {
      ...this.state,
      rtc: {
        ...this.state.rtc,
        joined
      }
    }
  }

  // api发送消息
  async sendChannelMessage(args) {
    await eduApi.sendChannelMessage({
      roomId: this.state.course.roomId,
      message: args.message,
      type: 1
    })
  }

  // SDK发送聊天消息
  async sendChatMessage(args) {
    const msgData = {
      userId: this.state.me.userId,
      userName: this.state.me.account,
      type: 1,
      message: args.message,
      userAvatar: args.userAvatar
    }
    await this.rtmClient.notifyMessage({
      cmd: ChatCmdType.chat,
      data: msgData,
      enableHistoricalMessaging: false
    })
  }

  // 更新即时聊天
  updateChannelMessage(msg) {
    this.state = {
      ...this.state,
      ...this.state.messages.push(msg)
    }
  }

  // 更新房间信息
  updateRoomInfo(data) {
    this.state = {
      ...this.state,
      course: {
        ...this.state.course,
        lockBoard: data.lockBoard,
        muteChat: data.muteAllChat,
        courseState: data.courseState,
        startTime: data.startTime
      }
    }
  }

  // 更新人员进出
  updateRoomMember(memberCount) {
    this.state = {
      ...this.state,
      course: {
        ...this.state.course,
        memberCount
      }
    }
  }

  // 更新连麦用户
  updateCoVideoUsers(rawUsers) {
    let coVideoUids = []
    let users = []
    rawUsers.forEach(item => {
      if (item.role === 2) {
        coVideoUids.push(item.uid)
      }
      users.push({
        role: item.role,
        account: item.userName,
        uid: item.uid,
        video: item.enableVideo,
        audio: item.enableAudio,
        chat: item.enableChat,
        grantBoard: item.grantBoard,
        userId: item.userId,
        screenId: item.screenId
      })
    })

    let meState = this.state.me
    let newMe = users.find(user => +this.state.me.uid === +user.uid)
    if (newMe) {
      meState = this.compositeMe({ ...newMe, coVideo: 1 })
    } else {
      meState = {
        ...meState,
        coVideo: 0
      }
    }

    let courseState = {
      screenId: this.state.course.screenId,
      teacherId: this.state.course.teacherId
    }

    let newCourse = users.find(user => +user.role === 1)

    if (newCourse) {
      courseState.screenId = newCourse.screenId
      courseState.teacherId = newCourse.uid
    }

    let applyUser = this.state.applyUser

    if (applyUser && applyUser.userId) {
      const foundUser = users.find(user => user.userId === applyUser.userId)
      if (!foundUser) {
        applyUser = {
          userId: '',
          account: ''
        }
      }
    }

    this.state = {
      ...this.state,
      users: users,
      me: {
        ...this.state.me,
        ...meState
      },
      course: {
        ...this.state.course,
        ...courseState,
        coVideoUids
      },
      applyUser
    }
  }

  // 分享状态发生改变了
  updateScreenShare(payload) {
    this.state = {
      ...this.state,
      course: {
        ...this.state.course,
        screenId: payload.screenId
      }
    }
  }

  // 录制状态发生改变了
  updateRecordState(params) {
    this.state = {
      ...this.state,
      course: {
        ...this.state.course,
        roomId: params.roomId,
        recordId: params.recordId,
        recordingTime: params.recordingTime,
        isRecording: !!params.isRecording
      }
    }
  }

  // 点对点消息
  async handlePeerMessage(body, peerId) {
    const {
      cmd,
      data: { userName, userId, type }
    } = body
    if (cmd !== 1) return
    // 当对端是老师的时候
    switch (type) {
      case CoVideoType.teacherSendReject: {
        globalStore.showToast({
          type: 'co-video',
          message: t('toast.teacher_reject_co_video')
        })
        globalStore.showVideo(true)
        return
      }
      case CoVideoType.studentSendApply: {
        if (globalStore.state.notice.reason === 'peer_hands_up' && this.state.applyUser.userId) {
          // 当已经收到消息以后屏蔽这条"举手申请"
          console.warn(`ignore: `, peerId, userId, type, userName)
          return
        }
        const applyUserId = userId
        if (applyUserId && peerId) {
          this.updateApplyUser({
            account: `${userName}`,
            userId: `${applyUserId}`
          })
          globalStore.showNotice({
            reason: 'peer_hands_up',
            text: t('notice.student_interactive_apply', { reason: `${userName}` })
          })
          globalStore.showToast({
            type: 'peer_hands_up',
            message: t('toast.student_send_co_video_apply', { reason: `${userName}` })
          })
          return
        }
        return
      }
      case CoVideoType.teacherSendAccept: {
        globalStore.showToast({
          type: 'peer_hands_up',
          message: t('toast.teacher_accept_co_video', { reason: `${userName}` })
        })
        globalStore.showVideo(false)
        return
      }
      case CoVideoType.teacherSendStop: {
        globalStore.showToast({
          type: 'peer_hands_up',
          message: t('toast.stop_co_video', { reason: `${userName}` })
        })
        globalStore.showVideo(true)
        return
      }
    }
  }

  // 打开音频/视频
  mute(uid, type) {
    const me = this.state.me
    if (me.uid === uid) {
      if (type === 'audio') {
        this.updateLocalMe({
          audio: 0,
          broad: true
        })
      }
      if (type === 'video') {
        this.updateLocalMe({
          video: 0,
          broad: true
        })
      }
      if (type === 'chat') {
        this.updateLocalMe({
          chat: 0,
          broad: true
        })
      }
    } else if (me.role === 1) {
      if (type === 'audio') {
        this.updateUserBy(`${uid}`, { audio: 0 })
      }
      if (type === 'video') {
        this.updateUserBy(`${uid}`, { video: 0 })
      }
      if (type === 'chat') {
        this.updateUserBy(`${uid}`, { chat: 0 })
      }
      if (type === 'grantBoard') {
        this.updateUserBy(`${uid}`, { grantBoard: 0 })
      }
    }
  }

  // 关闭音频/视频
  unmute(uid, type) {
    const me = this.state.me
    if (me.uid === uid) {
      if (type === 'audio') {
        this.updateLocalMe({
          audio: 1,
          broad: true
        })
      }
      if (type === 'video') {
        this.updateLocalMe({
          video: 1,
          broad: true
        })
      }
      if (type === 'chat') {
        this.updateLocalMe({
          chat: 1,
          broad: true
        })
      }
    } else if (me.role === 1) {
      if (type === 'audio') {
        this.updateUserBy(`${uid}`, { audio: 1 })
      }
      if (type === 'video') {
        this.updateUserBy(`${uid}`, { video: 1 })
      }
      if (type === 'chat') {
        this.updateUserBy(`${uid}`, { chat: 1 })
      }
      if (type === 'grantBoard') {
        this.updateUserBy(`${uid}`, { grantBoard: 1 })
      }
    }
  }

  updateApplyUser(user) {
    this.state = {
      ...this.state,
      applyUser: user
    }
  }

  // 更新用户信息
  async updateLocalMe(params) {
    const { broad, ...meParams } = params
    const newMe = this.compositeMe(meParams)
    const newMeAttrs = {
      uid: newMe.uid,
      account: newMe.account,
      role: newMe.role,
      video: newMe.video,
      audio: newMe.audio,
      chat: newMe.chat,
      grantBoard: newMe.grantBoard,
      coVideo: newMe.coVideo,
      userId: newMe.userId,
      screenId: newMe.screenId
    }

    const userAttrsParams = {
      userId: newMeAttrs.userId,
      enableChat: newMeAttrs.chat,
      enableAudio: newMeAttrs.audio,
      enableVideo: newMeAttrs.video,
      grantBoard: newMeAttrs.grantBoard
    }

    if (broad) {
      await this.updateRoomUser({ user: userAttrsParams })
    }
    this.state.users.forEach((item, index) => {
      if (item.uid == +this.state.me.uid) {
        this.state.users[index] = {
          ...newMeAttrs
        }
      }
    })
    this.state = {
      ...this.state,
      me: {
        ...this.state.me,
        ...newMeAttrs
      }
      // users: this.state.users.set(`${this.state.me.uid}`, newMeAttrs)
    }
  }

  async fetchCurrentRoom() {
    try {
      const res = await eduApi.fetchRoomBy(roomStore.state.course.roomId)
      const { course, me, users: rawUsers, appID, onlineUsers } = res

      let users = []
      rawUsers.forEach(it => {
        users.push({
          role: it.role,
          account: it.userName,
          uid: it.uid,
          video: it.enableVideo,
          audio: it.enableAudio,
          chat: it.enableChat,
          grantBoard: it.grantBoard,
          userId: it.userId,
          screenId: it.screenId
        })
      })

      await this.rtmClient.login(appID, `${me.uid}`, me.rtmToken)
      await this.rtmClient.join(course.rid)
      this.state = {
        ...this.state,
        rtm: {
          ...this.state.rtm,
          joined: true
        },
        course: {
          ...this.state.course,
          rid: course.channelName,
          roomType: course.roomType,
          roomId: course.roomId,
          roomName: course.roomName,
          courseState: course.courseState,
          muteChat: course.muteAllChat,
          recordId: `${course.recordId}`,
          isRecording: course.isRecording,
          recordingTime: course.recordingTime,
          lockBoard: course.lockBoard,
          boardId: `${course.boardId}`,
          boardToken: `${course.boardToken}`,
          teacherId: `${course.teacherId}`,
          screenId: `${me.screenId}`,
          screenToken: `${me.screenToken}`,
          coVideoUids: course.coVideoUids,
          startTime: course.startTime,
          memberCount: +onlineUsers
        },
        me: {
          ...this.state.me,
          uid: me.uid,
          account: me.userName,
          rtmToken: me.rtmToken,
          rtcToken: me.rtcToken,
          channelName: me.channelName,
          screenId: me.screenId,
          screenToken: me.screenToken,
          appID: me.appID,
          role: me.role,
          chat: me.enableChat,
          video: me.enableVideo,
          audio: me.enableAudio,
          userId: me.userId,
          coVideo: me.coVideo
        },
        users,
        appID
      }
    } catch (err) {
      if (this.rtmClient._logged) {
        await this.rtmClient.logout()
      }
      throw err
    }
  }

  addLocalStream(stream) {
    this.state = {
      ...this.state,
      rtc: {
        ...this.state.rtc,
        localStream: stream
      }
    }
  }

  removeLocalStream() {
    this.state = {
      ...this.state,
      rtc: {
        ...this.state.rtc,
        localStream: null,
        localSharedStream: null
      }
    }
  }

  addLocalSharedStream(stream) {
    this.state = {
      ...this.state,
      rtc: {
        ...this.state.rtc,
        localSharedStream: stream
      }
    }
  }

  removeLocalSharedStream() {
    this.state = {
      ...this.state,
      rtc: {
        ...this.state.rtc,
        localSharedStream: null
      }
    }
  }

  // 添加rtc用户
  addRTCUser(uid) {
    this.state = {
      ...this.state,
      rtc: {
        ...this.state.rtc,
        ...this.state.rtc.users.push(uid)
      }
    }
  }

  // 删除用户
  removePeerUser(uid) {
    this.state.rtc.users.forEach((item, index) => {
      if (item == uid) {
        this.state.rtc.users.splice(index, 1)
      }
    })

    this.state = {
      ...this.state,
      rtc: {
        ...this.state.rtc,
        ...this.state.rtc.users
      }
    }
  }

  addRemoteStream(stream) {
    this.state = {
      ...this.state,
      rtc: {
        ...this.state.rtc,
        ...this.state.rtc.remoteStreams.push(stream)
      }
    }
  }

  removeRemoteStream(uid) {
    let remoteStream = null
    this.state.rtc.remoteStreams.forEach((item, index) => {
      if (item.uid == uid) {
        remoteStream = item
        this.state.rtc.remoteStreams.splice(index, 1)
      }
    })

    if (remoteStream && remoteStream.stream && remoteStream.stream.isPlaying) {
      remoteStream.stream.isPlaying() && remoteStream.stream.stop()
    }

    this.state = {
      ...this.state,
      rtc: {
        ...this.state.rtc,
        ...this.state.rtc.remoteStreams
      }
    }
  }

  updateRtc(newState) {
    this.state = {
      ...this.state,
      rtc: {
        ...this.state.rtc,
        ...newState
      }
    }
  }

  updateDevice(state) {
    this.state = {
      ...this.state,
      mediaDevice: state
    }
    this.commit(this.state)
  }

  // 更新用户状态
  async updateUserBy(uid, params) {
    const { broad = true, ...userParams } = params
    let prevUser = {}
    this.state.users.forEach(item => {
      if (item.uid == +uid) {
        prevUser = {
          ...item,
          ...userParams
        }
      }
    })

    const newUserAttrs = {
      ...prevUser
    }

    const userAttrsParams = {
      userId: newUserAttrs.userId,
      enableChat: newUserAttrs.chat,
      enableAudio: newUserAttrs.audio,
      enableVideo: newUserAttrs.video,
      grantBoard: newUserAttrs.grantBoard
    }
    if (broad) {
      await this.updateRoomUser({ user: userAttrsParams })
    }
    this.state.users.forEach((item, index) => {
      if (item.uid == +uid) {
        this.state.users[index] = {
          ...newUserAttrs
        }
      }
    })

    this.state = {
      ...this.state
    }
  }

  compositeMe(params) {
    console.log('compositeMe: ', params)
    const newMe = { ...this.state.me, ...params }
    return newMe
  }

  resolveCourseAttrsToOperate(params) {
    const keys = ['lockBoard', 'courseState', 'muteChat']
    for (let key of keys) {
      // eslint-disable-next-line no-prototype-builtins
      if (params.hasOwnProperty(key)) {
        let value = -1
        let stateValue = params[key]
        let stateKey = key
        if (key === 'lockBoard') {
          value = stateValue ? RoomMessage.lockBoard : RoomMessage.unlockBoard
        } else if (key === 'courseState') {
          value = stateValue ? RoomMessage.startCourse : RoomMessage.endCourse
        } else if (key === 'muteChat') {
          value = stateValue ? RoomMessage.muteAllChat : RoomMessage.unmuteAllChat
          stateKey = 'muteAllChat'
        }
        return {
          key: stateKey,
          stateValue,
          value
        }
      }
    }
  }

  async updateCourse(params) {
    const { broad = true, ...courseParams } = params
    const { key, stateValue } = this.resolveCourseAttrsToOperate(courseParams)
    if (broad) {
      await eduApi.updateCourse({
        room: {
          [`${key}`]: stateValue
        }
      })
      this.state = {
        ...this.state,
        course: {
          ...this.state.course,
          ...courseParams
        }
      }
      return
    }
    this.state = {
      ...this.state,
      course: {
        ...this.state.course,
        ...courseParams
      }
    }
  }

  // 白板
  async getCourseRecordBy(recordId, roomId, userToken) {
    if (!this.state) return
    if (this.state.courseRecordData.status === 2 && this.state.courseRecordData.url) return
    const { boardId, startTime, endTime, url, status, statusText } = await eduApi.getCourseRecordBy(
      recordId,
      roomId,
      userToken
    )
    this.state = {
      ...this.state,
      courseRecordData: {
        startTime,
        endTime,
        url,
        boardId,
        status,
        statusText
      }
    }
  }

  // 更新用户状态
  async updateRoomUser({ user }) {
    return await eduApi.updateRoomUser(user)
  }

  // 退出房间
  async exitRoom() {
    return await eduApi.exitRoom(this.state.course.roomId)
  }

  // 结束直播
  async exitAll() {
    try {
      try {
        await this.rtmClient.exit()
      } catch (err) {
        console.warn(err)
      }
      try {
        await this.rtcClient.exit()
      } catch (err) {
        console.warn(err)
      }
    } finally {
      sessionStorage.removeItem('agora_room') // 清除缓存
      this.state = {
        ...this.defaultState
      }
    }
  }
}

export const roomStore = new RoomStore()
